const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  state = PENDING;
  result;
  // 用于存储暂时不能执行的回调，格式形如：[ { onFulfilled, onRejected } ]
  handlers = [];

  constructor(executor) {
    const resolve = (result) => {
      // result 如果是promise（它），就等它的状态改变，当前Promise受它的影响

      if (result instanceof MyPromise) {
        result
          .then(
            this.#changeState.bind(this, FULFILLED),
            // (value) => {
            //   this.#changeState(FULFILLED, value);
            //   // this.#runHandlers();
            // },
            this.#changeState.bind(this, REJECTED)
            // (reason) => {
            //   this.#changeState(REJECTED, reason);
            //   // this.#runHandlers();
            // }
          )
          .finally(this.#runHandlers.bind(this));
        // .finally(() => {
        //   this.#runHandlers();
        // });
      } else {
        this.#changeState(FULFILLED, result);
        this.#runHandlers();
      }
    };

    const reject = (result) => {
      // 就算result是promise，reject也不管，一视同仁，直接作为结果传下去
      this.#changeState(REJECTED, result);
      this.#runHandlers();
    };

    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  #changeState(state, result) {
    // 状态一旦改变，就不能再变
    if (this.state !== PENDING) return;

    this.state = state;
    this.result = result;
  }

  then(onFulfilled, onRejected) {
    // 做默认值处理
    if (typeof onFulfilled !== 'function') {
      onFulfilled = (value) => value;
    }
    if (typeof onRejected !== 'function') {
      onRejected = (reason) => {
        throw reason;
      };
    }

    return new MyPromise((resolve, reject) => {
      if (this.state === FULFILLED) {
        this.#runHandler(onFulfilled, resolve, reject);
      } else if (this.state === REJECTED) {
        this.#runHandler(onRejected, resolve, reject);
      } else {
        this.handlers.push({ onFulfilled, onRejected, resolve, reject });
      }
    });
  }

  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  finally(onFinally) {
    // return this.then(onFinally, onFinally)

    // return this.then((value) => {
    //   onFinally(value)
    // }, (reason) => {
    //   onFinally(reason)
    // })

    return this.then(
      () => {
        onFinally();
      },
      () => {
        onFinally();
      }
    );
  }

  #runHandlers() {
    while (this.handlers.length > 0) {
      const { onFulfilled, onRejected, resolve, reject } = this.handlers.shift();
      if (this.state === FULFILLED) {
        this.#runHandler(onFulfilled, resolve, reject);
      } else {
        this.#runHandler(onRejected, resolve, reject);
      }
    }
  }

  #runHandler(callback, resolve, reject) {
    queueMicrotask(() => {
      let value;

      try {
        value = callback(this.result);
      } catch (err) {
        reject(err);
        return;
      }

      // 如果回调返回的又是一个MyPromise（它），
      // 那么新new出来的MyPromise（我）就要受这个返回的MyPromise影响
      // 它成功，我就成功，它失败，我就失败
      if (value instanceof MyPromise) {
        // value.then((value) => {
        //   resolve(value)
        // }, (reason) => {
        //   reject(reason)
        // })
        value.then(resolve, reject);
      } else {
        resolve(value);
      }
    });
  }

  // 三要素：入口（参数），出口（返回值），功能
  static resolve(value) {
    if (value instanceof MyPromise) return value;

    return new MyPromise((resolve) => {
      resolve(value);
    });
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      if (Array.isArray(promises)) {
        for (let i = 0, len = promises.length; i < len; i++) {
          MyPromise.resolve(promises[i]).then(resolve, reject);
        }
      } else {
        reject(new TypeError(`${typeof promises} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`));
      }
    });
  }

  static all(promises) {
    // 难点：
    // 1. 保证成功的值的顺序
    // 2. 等待所有都处理完（看添加的value的个数是否等于promises的长度）

    return new MyPromise((resolve, reject) => {
      if (Array.isArray(promises)) {
        const values = [];
        // 保存真实的添加的value的个数，因为values.length不准确，前面有可能为空元素占位
        let count = 0;

        for (let i = 0, len = promises.length; i < len; i++) {
          MyPromise.resolve(promises[i])
            .then((value) => {
              values[i] = value;
              // 每次添加一个value，就让count+1
              count++;
              // 要去判断，是不是最后一个，如果是，则可以调用resolve了
              // if (count === len) {
              //   resolve(values);
              // }
              count === len && resolve(values);
            })
            .catch(reject);
        }
      } else {
        reject(new TypeError(`${typeof promises} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`));
      }
    });
  }

  static allSettled(promises) {
    // 难点：
    // 1. 保证成功的值的顺序
    // 2. 等待所有都处理完（看添加的value的个数是否等于promises的长度）

    return new MyPromise((resolve, reject) => {
      if (Array.isArray(promises)) {
        const values = [];
        // 保存真实的添加的value的个数，因为values.length不准确，前面有可能为空元素占位
        let count = 0;

        for (let i = 0, len = promises.length; i < len; i++) {
          MyPromise.resolve(promises[i])
            .then((value) => {
              values[i] = { state: FULFILLED, value };
            })
            .catch((reason) => {
              values[i] = { state: REJECTED, reason };
            })
            .finally(() => {
              // 每次添加一个value，就让count+1
              count++;
              // 要去判断，是不是最后一个，如果是，则可以调用resolve了
              count === len && resolve(values);
            });
        }
      } else {
        reject(new TypeError(`${typeof promises} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`));
      }
    });
  }

  static any(promises) {
    return new MyPromise((resolve, reject) => {
      if (Array.isArray(promises)) {
        const errors = [];
        let count = 0;
        for (let i = 0, len = promises.length; i < len; i++) {
          MyPromise.resolve(promises[i])
            .then(resolve)
            .catch((reason) => {
              errors[i] = reason;
            })
            .finally(() => {
              count++;
              count === len && reject(new AggregateError(errors, 'All promises were rejected'));
            });
        }
      } else {
        reject(new TypeError(`${typeof promises} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`));
      }
    });
  }

  static resolveDelay(value, ms) {
    return new MyPromise((resolve) => {
      setTimeout(resolve, ms, value);
    });
  }

  static rejectDelay(reason, ms) {
    return new MyPromise((resolve, reject) => {
      setTimeout(reject, ms, reason);
    });
  }

  static try(func, ...args) {
    return new MyPromise((resolve) => {
      resolve(func(...args));
    });
    // let result;
    // try {
    //   result = func(...args);
    // } catch (err) {
    //   return MyPromise.reject(err);
    // }
    // return MyPromise.resolve(result);
  }
}
